﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;

namespace DARE
{
    [AttrTypeDependentSerializable(typeof(CContainerLineData))]
    public class CContainerLine : CWidget, IHudContainerGroup
    {
        #region identifier

        /// <summary>
        /// this delegate will be called when the ContainerLine is full and we try to attach an item on it.
        /// Prototype for this delegate:
        /// bool fullLine(CContainerLine sender, object data);
        /// </summary>
        /// <param name="sender">this will be the containerLine that calls this delegate</param>
        /// <param name="data"></param>
        /// <returns>return true if you want the item to be destroyed</returns>
        public delegate bool fullLineEventHandler(CContainerLine sender, object data);

        #endregion

        #region fields

        private List<CWidget> m_elems;
        private CHudTexture m_elemsTexture;
        private int m_nbByLine;
        private bool m_conserveAspect = true;
        private Vector2 m_itemSize;
        private float m_interval;
        /// <summary>
        /// Defines every types of vignettes (CVignette.Type) that is allowed on this ContainerLine.
        /// For example, if you use this ContainerLine for skillbar and the vignette represents a equipment
        /// and the type of the vignette defines the equipment (as sword) type, the vignette will not be able to be
        /// attached on it.
        /// </summary>
        private List<long> m_allowedTypes;

        #endregion

        #region properties

        /// <summary>
        /// Represents the event invocated whenever the CContainerLine is full (every slots are filled).
        /// </summary>
        public event fullLineEventHandler FullLineEventHandler;

        /// <summary>
        /// Gets/Sets the texture of each slot in this CContainerLine.
        /// </summary>
        public CHudTexture ElementsTexture { get { return m_elemsTexture; } set { m_elemsTexture = value; foreach (CWidget e in m_elems) e.Texture = value.Clone(); } }

        /// <summary>
        /// Gets/Sets the interval between each slot in this CContainerLine
        /// </summary>
        public float Interval
        {
            get { return m_interval; }
            set
            {
                m_interval = value;
                for (int i = 0; i < m_nbByLine; ++i)
                    m_elems[i].Position = new Vector2(i * (ItemsSize.X + m_interval), 0);
            }
        }

        /// <summary>
        /// Set this value to true if you want that the contained widget conserve their aspects.
        /// Set this value to false if you want that the contained widget take the total size of the its slot not regarding its original size.
        /// there are risks for the contained widgets to be ugly if this value is set to false.
        /// </summary>
        public bool ConserveAspect { get { return m_conserveAspect; } set { m_conserveAspect = value; } }

        /// <summary>
        /// Gets/Sets the size of each slot in this container
        /// </summary>
        public Vector2 ItemsSize
        {
            get { return m_itemSize; }
            set
            {
                m_itemSize = value;
                for (int i = 0; i < m_elems.Count; ++i)
                {
                    m_elems[i].Size = new Vector2(m_itemSize.X, m_itemSize.Y);
                    m_elems[i].Position = new Vector2(i * (m_itemSize.X + m_interval), 0);
                }
            }
        }

        /// <summary>
        /// Gets/Sets the number of slots by line.
        /// Will automatically add lines relatively to the number of containers by line and of the number of elements.
        /// </summary>
        public int NbByLine
        {
            get { return m_nbByLine; }
            set
            {
                if (value == m_nbByLine)
                    return;
                foreach (CWidget wid in m_children.Values)
                    HUD.Remove(wid);
                m_children.Clear();
                foreach (CWidget wid in m_elems)
                    HUD.Remove(wid);
                m_elems.Clear();
                m_nbByLine = value;
                for (int i = 0; i < m_nbByLine; ++i)
                    AddElem();
            }
        }

        /// <summary>
        /// Defines every types of vignettes (CVignette.Type) that is allowed on this ContainerLine.
        /// For example, if you use this ContainerLine for skillbar and the vignette represents a equipment
        /// and the type of the vignette defines the equipment (as sword) type, the vignette will not be able to be
        /// attached on it.
        /// </summary>
        public List<long> AllowedTypes { get { return m_allowedTypes; } set { m_allowedTypes = value; } }

        #endregion

        #region ctor

        public CContainerLine(DARE.CHUDSystem hud, CWidget parent, string name)
            : base(hud, parent, name)
        {
            IsContainerGroup = true;
            m_elems = new List<CWidget>();
            m_itemSize = new Vector2(20, 20);
            m_elemsTexture = HUD.Skinner.GetTexture("container");
            m_nbByLine = 5;
            m_interval = 2;
            for (int i = 0; i < m_nbByLine; ++i)
                AddElem();
            m_allowedTypes = new List<long>();
            Size = new Vector2(200, 20);
        }

        #endregion

        #region clone

        public override CWidget Clone()
        {
            CWidget w = new CContainerLine(HUD, null, m_name + "_clone" + m_nbClones);
            ++m_nbClones;
            (w as CContainerLine).m_elemsTexture = m_elemsTexture;
            (w as CContainerLine).m_allowedTypes.Clear();
            foreach (int type in m_allowedTypes)
                (w as CContainerLine).m_allowedTypes.Add(type);
            (w as CContainerLine).FullLineEventHandler = FullLineEventHandler;
            (w as CContainerLine).m_conserveAspect = m_conserveAspect;
            (w as CContainerLine).m_nbByLine = m_nbByLine;
            (w as CContainerLine).m_conserveAspect = m_conserveAspect;
            (w as CContainerLine).m_itemSize = m_itemSize;
            (w as CContainerLine).m_interval = m_interval;
            Clone(ref w);
            //            (w as CVignette).IsProduceer = false;
            return w;
        }

        #endregion

        #region utils

        /// <summary>
        /// Gets the list of each CVignette contained.
        /// </summary>
        public List<CVignette> GetAllItems()
        {
            List<CVignette> vig = new List<CVignette>();
            foreach (CWidget wid in m_elems)
                if (wid.IsContainer && wid.Children.Count > 0)
                    vig.Add(wid.Children.First().Value as CVignette);
            return vig;
        }

        public CVignette TakeItem(CVignette vig)
        {
            if (m_elems.Contains(vig))
            {
                m_elems.Remove(vig);
                return vig;
            }
            return null;
        }

        /// <summary>
        /// Gets the list of each CVignette ids contained
        /// </summary>
        public List<int> GetAllItemID()
        {
            List<int> vig = new List<int>();
            foreach (CWidget wid in m_elems)
                if (wid.IsContainer && wid.Children.Count > 0)
                    vig.Add((wid.Children.First().Value as CVignette).ID);
            return vig;
        }

        /// <summary>
        /// Checks if the type identifier given in parameters is allowed
        /// </summary>
        /// <param name="type">the identifier of the type of item</param>
        public bool IsTypeAllowed(long type)
        {
            return m_allowedTypes.Contains(type);
        }

        /// <summary>
        /// This attachs a containable widget to a container in a widget that is a container group (`IsContainerGroup = true`)
        /// </summary>
        /// <param name="from">This is the widget to be attached</param>
        /// <param name="to">Can be a cumulable widget to which we want to attach the "from" widget. For example, if you drop a Vignette on another representing the same item, and if this vignette is cumulable, set the give the vignette on which you will attach the first one.</param>
        public override bool Attach(CWidget from, CWidget to)
        {
            if (m_parent.IsContainerGroup)
                return m_parent.Attach(from, to);
            bool isAttached = false;

            for (int i = 0; i < m_elems.Count; ++i)
                if (m_elems.ElementAt(i).Children.Count > 0 && (m_elems.ElementAt(i).Children.First().Value as CVignette).ID == (from as CVignette).ID)
                {
                    CVignette vigDest = m_elems.ElementAt(i).Children.First().Value as CVignette;
                    CVignette vigSrc = from as CVignette;
                    if (vigDest != null && vigDest.IsCumulable)
                    {
                        if (vigDest.Cumulated + vigSrc.Cumulated <= vigDest.CumulLimit)
                        {
                            vigDest.Cumulated += vigSrc.Cumulated;
                            vigDest.BottomRightText.Text = vigDest.Cumulated.ToString();
                            vigSrc.LastAttachedTo = null;
                            return false;
                        }
                    }
                }

            for (int i = 0; i < m_elems.Count; ++i)
            {
                if (m_elems.ElementAt(i).Children.Count == 0)
                {
                    if (HUD.Widgets.ContainsValue(from))
                        HUD.Remove(from.Name);
                    from.Parent = m_elems.ElementAt(i);
                    if (from.Parent.Children.ContainsKey(from.Name))
                        from.Parent.Children.Remove(from.Name);
                    from.Parent.Children.Add(from.Name, from);
                    from.Actualize();
                    isAttached = true;
                    break;
                }
            }
            if (!isAttached)
            {
                bool destroy = true;
                if (FullLineEventHandler != null)
                    destroy = FullLineEventHandler.Invoke(this, null);
                if (!destroy)
                    return false;
                if (!HUD.Widgets.ContainsValue(from))
                    HUD.Add(from);
                from.Hide();
            }
            return isAttached;
        }

        public override bool Attach(CWidget from)
        {
            return Attach(from, null);
        }

        //private int elemPos = 0;
        private void AddElem()
        {
            CWidget wid = HUD.GetElement(m_name + "_elem_" + m_elems.Count);
            if (wid == null)
                wid = new CWidget(HUD, this, m_name + "_elem_" + m_elems.Count);
            m_elems.Add(wid);
            m_elems[m_elems.Count - 1].IsContainer = true;
            m_elems[m_elems.Count - 1].Size = new Vector2(m_itemSize.X, m_itemSize.Y);
            m_elems[m_elems.Count - 1].Position = new Vector2((m_elems.Count - 1) * (m_itemSize.X + m_interval), 0);
            if (m_elemsTexture != null)
                m_elems[m_elems.Count - 1].Texture = m_elemsTexture.Clone();
         //   ++elemPos;
        }

        /// <summary>
        /// Gets the item at the given position
        /// </summary>
        public CWidget elementAt(int pos)
        {
            if (pos < m_elems.Count)
                return m_elems[pos];
            return null;
        }

        #endregion
    }
}
